#include <ximera/graphics/ogre/graphicssystem.hpp>
#include <ximera/graphics/ogre/world.hpp>
#include <ximera/common/utils.hpp>

#include <boost/lexical_cast.hpp>

#include "OgreConfigFile.h"

#if defined(XIMERA_WIN32)
#include "windows.h"
#endif

using namespace Ogre;

namespace ximera { namespace graphics { namespace ogre 
{
    GraphicsSystem::GraphicsSystem(system::Application& app)
        : _application(app),
          _ogreRoot(),
          _ogreRenderSystem(0),
          _ogreWindow(0),
          _ogreSceneManager(0)
    {
        try
        {
            #if defined(XIMERA_DEBUG)
            _ogreRoot.reset(new Root("ximera.ogre.plugins_d.cfg", "ximera.ogre.cfg", "ximera.ogre.log"));
            #else
            _ogreRoot.reset(new Root("ximera.ogre.plugins.cfg", "ximera.ogre.cfg", "ximera.ogre.log"));
            #endif

            setupResources();
            setupRenderer();
            initWindow();
            chooseSceneManager();
            loadResources();            
        }
        catch (Ogre::Exception& e)
        {
            XIMERA_THROW_OGRE_EXCEPTION(e);
        }
    }

    graphics::IWorld* GraphicsSystem::createWorld()
    {
        return new World(this);
    }

    void GraphicsSystem::render()
    {
        #if defined(XIMERA_WIN32)
        MSG msg;
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        #endif

        _ogreWindow->update();
        
        if (!_ogreRoot->renderOneFrame() || _ogreWindow->isClosed())
            _application.shouldClose(true);
    }

    void GraphicsSystem::ambientLight(math::Color const& color)
    {
        _ogreSceneManager->setAmbientLight(XIMERA_TO_OGRE_COLOR(color));
    }

    void GraphicsSystem::skyDome(String const& materialName)
    {
        _ogreSceneManager->setSkyDome(true, string_util::wstr2str(materialName));
    }

    bool GraphicsSystem::isSkyDomeEnabled() const
    {
        return _ogreSceneManager->isSkyDomeEnabled();
    }

    void GraphicsSystem::disableSkyDome()
    {
        _ogreSceneManager->setSkyDome(false, "");
    }

    void GraphicsSystem::skyBox(String const& materialName)
    {
        _ogreSceneManager->setSkyBox(true, string_util::wstr2str(materialName));
    }

    bool GraphicsSystem::isSkyBoxEnabled() const
    {
        return _ogreSceneManager->isSkyBoxEnabled();
    }

    void GraphicsSystem::disableSkyBox()
    {
        _ogreSceneManager->setSkyBox(false, "");
    }

    void GraphicsSystem::setupResources()
    {   
        // Load resource paths from config file
        Ogre::ConfigFile cf;
        cf.load("ximera.ogre.resources.cfg");

        // Go through all sections & settings in the file
        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

        Ogre::String secName, typeName, archName;
        while (seci.hasMoreElements())
        {
            secName = seci.peekNextKey();
            Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
            Ogre::ConfigFile::SettingsMultiMap::iterator i;
            for (i = settings->begin(); i != settings->end(); ++i)
            {
                typeName = i->first;
                archName = i->second;
                Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
                    archName, typeName, secName);
            }
        }
    }

    void GraphicsSystem::setupRenderer()
    {
        if (_application.settings().value(L"ximera.ogre.showdialog") == L"1")
        {
            if (!_ogreRoot->showConfigDialog())
                XIMERA_THROW(OgreException, L"Ogre::Root::showConfigDialog returned false");

            _ogreRoot->saveConfig();

            _ogreRenderSystem = _ogreRoot->getRenderSystem();
        }
        else
        {
            RenderSystemList* renderers = _ogreRoot->getAvailableRenderers();
            if (renderers->empty())
                XIMERA_THROW(OgreException, L"Could not find any renderers!");

            for (RenderSystemList::iterator it = renderers->begin(); it != renderers->end(); ++it)
            {
                _ogreRenderSystem = *it;
                if (_ogreRenderSystem->getName().find("GL") != std::string::npos)
                    break;
            }

            _ogreRoot->setRenderSystem(_ogreRenderSystem);
        }
    }

    void GraphicsSystem::chooseSceneManager()
    {
        _ogreSceneManager = _ogreRoot->createSceneManager(ST_GENERIC, "XimeraSceneManager");
        _ogreSceneManager->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

        _application.settings().value(L"ximera.ogre.scenemanager", boost::lexical_cast<String>(reinterpret_cast<Int64>(_ogreSceneManager)));
    }

    void GraphicsSystem::initWindow()
    {
        String windowTitle = _application.settings().value(L"ximera.window.title");
        if (windowTitle.empty())
            windowTitle = L"Ximera Application";

        _ogreWindow = _ogreRoot->initialise(true, string_util::wstr2str(windowTitle));        

        #if defined(XIMERA_WIN32)
        HWND hwnd = 0;
        _ogreWindow->getCustomAttribute("WINDOW", &hwnd);

        _application.settings().value(L"ximera.window.handle", boost::lexical_cast<String>(reinterpret_cast<Int64>(hwnd)));
        _application.settings().value(L"ximera.ogre.renderwindow", boost::lexical_cast<String>(reinterpret_cast<Int64>(_ogreWindow)));
        #endif
    }

    void GraphicsSystem::loadResources()
    {
        ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
    }
}}}
